package wechatsmart

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/sha1"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io"
	"math/rand"
	mathRand "math/rand"
	"sort"
	"strconv"
	"strings"
	"time"
	"yundian/internel/pkg/dingo/global"

	"yundian/internel/pkg/authorized"
)

const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

const (
	ValidateSignatureError int = -40001
	ParseXmlError          int = -40002
	ComputeSignatureError  int = -40003
	IllegalAesKey          int = -40004
	ValidateCorpidError    int = -40005
	EncryptAESError        int = -40006
	DecryptAESError        int = -40007
	IllegalBuffer          int = -40008
	EncodeBase64Error      int = -40009
	DecodeBase64Error      int = -40010
	GenXmlError            int = -40010
	ParseJsonError         int = -40012
	GenJsonError           int = -40013
	IllegalProtocolType    int = -40014
)

type ProtocolType int

const (
	XmlType ProtocolType = 0
)

type CryptError struct {
	ErrCode int
	ErrMsg  string
}

func NewCryptError(err_code int, err_msg string) *CryptError {
	return &CryptError{ErrCode: err_code, ErrMsg: err_msg}
}

type WXBizMsg4Recv struct {
	Tousername string `xml:"ToUserName"`
	Encrypt    string `xml:"Encrypt"`
	Agentid    string `xml:"AgentID"`
}

type CDATA struct {
	Value string `xml:",cdata"`
}

type WXBizMsg4Send struct {
	XMLName   xml.Name `xml:"xml"`
	Encrypt   CDATA    `xml:"Encrypt"`
	Signature CDATA    `xml:"MsgSignature"`
	Timestamp string   `xml:"TimeStamp"`
	Nonce     CDATA    `xml:"Nonce"`
}

func NewWXBizMsg4Send(encrypt, signature, timestamp, nonce string) *WXBizMsg4Send {
	return &WXBizMsg4Send{Encrypt: CDATA{Value: encrypt}, Signature: CDATA{Value: signature}, Timestamp: timestamp, Nonce: CDATA{Value: nonce}}
}

type ProtocolProcessor interface {
	parse(src_data []byte) (*WXBizMsg4Recv, *CryptError)
	serialize(msg_send *WXBizMsg4Send) ([]byte, *CryptError)
}

type WXBizMsgCrypt struct {
	token              string
	encoding_aeskey    string
	receiver_id        string
	protocol_processor ProtocolProcessor
}

type XmlProcessor struct {
}

func (self *XmlProcessor) parse(src_data []byte) (*WXBizMsg4Recv, *CryptError) {
	var msg4_recv WXBizMsg4Recv
	err := xml.Unmarshal(src_data, &msg4_recv)
	if nil != err {
		return nil, NewCryptError(ParseXmlError, "xml to msg fail")
	}
	return &msg4_recv, nil
}

func (self *XmlProcessor) serialize(msg4_send *WXBizMsg4Send) ([]byte, *CryptError) {
	xml_msg, err := xml.Marshal(msg4_send)
	if nil != err {
		return nil, NewCryptError(GenXmlError, err.Error())
	}
	return xml_msg, nil
}

func NewWXBizMsgCrypt(token, encoding_aeskey, receiver_id string, protocol_type ProtocolType) *WXBizMsgCrypt {
	var protocol_processor ProtocolProcessor
	if protocol_type != XmlType {
		panic("unsupport protocal")
	} else {
		protocol_processor = new(XmlProcessor)
	}

	return &WXBizMsgCrypt{token: token, encoding_aeskey: (encoding_aeskey + "="), receiver_id: receiver_id, protocol_processor: protocol_processor}
}

func (self *WXBizMsgCrypt) randString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
	}
	return string(b)
}

func (self *WXBizMsgCrypt) pKCS7Padding(plaintext string, block_size int) []byte {
	padding := block_size - (len(plaintext) % block_size)
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	var buffer bytes.Buffer
	buffer.WriteString(plaintext)
	buffer.Write(padtext)
	return buffer.Bytes()
}

func (self *WXBizMsgCrypt) pKCS7Unpadding(plaintext []byte, block_size int) ([]byte, *CryptError) {
	plaintext_len := len(plaintext)
	if nil == plaintext || plaintext_len == 0 {
		return nil, NewCryptError(DecryptAESError, "pKCS7Unpadding error nil or zero")
	}
	if plaintext_len%block_size != 0 {
		return nil, NewCryptError(DecryptAESError, "pKCS7Unpadding text not a multiple of the block size")
	}
	padding_len := int(plaintext[plaintext_len-1])
	return plaintext[:plaintext_len-padding_len], nil
}

func (self *WXBizMsgCrypt) cbcEncrypter(plaintext string) ([]byte, *CryptError) {
	aeskey, err := base64.StdEncoding.DecodeString(self.encoding_aeskey)
	if nil != err {
		return nil, NewCryptError(DecodeBase64Error, err.Error())
	}
	const block_size = 32
	pad_msg := self.pKCS7Padding(plaintext, block_size)

	block, err := aes.NewCipher(aeskey)
	if err != nil {
		return nil, NewCryptError(EncryptAESError, err.Error())
	}

	ciphertext := make([]byte, len(pad_msg))
	iv := aeskey[:aes.BlockSize]

	mode := cipher.NewCBCEncrypter(block, iv)

	mode.CryptBlocks(ciphertext, pad_msg)
	base64_msg := make([]byte, base64.StdEncoding.EncodedLen(len(ciphertext)))
	base64.StdEncoding.Encode(base64_msg, ciphertext)

	return base64_msg, nil
}

func (self *WXBizMsgCrypt) cbcDecrypter(base64_encrypt_msg string) ([]byte, *CryptError) {
	aeskey, err := base64.StdEncoding.DecodeString(self.encoding_aeskey)
	if nil != err {
		return nil, NewCryptError(DecodeBase64Error, err.Error())
	}

	encrypt_msg, err := base64.StdEncoding.DecodeString(base64_encrypt_msg)
	if nil != err {
		return nil, NewCryptError(DecodeBase64Error, err.Error())
	}

	block, err := aes.NewCipher(aeskey)
	if err != nil {
		return nil, NewCryptError(DecryptAESError, err.Error())
	}

	if len(encrypt_msg) < aes.BlockSize {
		return nil, NewCryptError(DecryptAESError, "encrypt_msg size is not valid")
	}

	iv := aeskey[:aes.BlockSize]

	if len(encrypt_msg)%aes.BlockSize != 0 {
		return nil, NewCryptError(DecryptAESError, "encrypt_msg not a multiple of the block size")
	}

	mode := cipher.NewCBCDecrypter(block, iv)

	mode.CryptBlocks(encrypt_msg, encrypt_msg)

	return encrypt_msg, nil
}

func (self *WXBizMsgCrypt) calSignature(timestamp, nonce, data string) string {
	sort_arr := []string{self.token, timestamp, nonce, data}
	sort.Strings(sort_arr)
	var buffer bytes.Buffer
	for _, value := range sort_arr {
		buffer.WriteString(value)
	}

	sha := sha1.New()
	sha.Write(buffer.Bytes())
	signature := fmt.Sprintf("%x", sha.Sum(nil))
	return string(signature)
}

func (self *WXBizMsgCrypt) ParsePlainText(plaintext []byte) ([]byte, uint32, []byte, []byte, *CryptError) {
	const block_size = 32
	plaintext, err := self.pKCS7Unpadding(plaintext, block_size)
	if nil != err {
		return nil, 0, nil, nil, err
	}

	text_len := uint32(len(plaintext))
	if text_len < 20 {
		return nil, 0, nil, nil, NewCryptError(IllegalBuffer, "plain is to small 1")
	}
	random := plaintext[:16]
	msg_len := binary.BigEndian.Uint32(plaintext[16:20])
	if text_len < (20 + msg_len) {
		return nil, 0, nil, nil, NewCryptError(IllegalBuffer, "plain is to small 2")
	}

	msg := plaintext[20 : 20+msg_len]
	receiver_id := plaintext[20+msg_len:]

	return random, msg_len, msg, receiver_id, nil
}

func (self *WXBizMsgCrypt) VerifyURL(msg_signature, timestamp, nonce, echostr string) ([]byte, *CryptError) {
	signature := self.calSignature(timestamp, nonce, echostr)

	if strings.Compare(signature, msg_signature) != 0 {
		return nil, NewCryptError(ValidateSignatureError, "signature not equal")
	}

	plaintext, err := self.cbcDecrypter(echostr)
	if nil != err {
		return nil, err
	}

	_, _, msg, receiver_id, err := self.ParsePlainText(plaintext)
	if nil != err {
		return nil, err
	}

	if len(self.receiver_id) > 0 && strings.Compare(string(receiver_id), self.receiver_id) != 0 {
		fmt.Println(string(receiver_id), self.receiver_id, len(receiver_id), len(self.receiver_id))
		return nil, NewCryptError(ValidateCorpidError, "receiver_id is not equil")
	}

	return msg, nil
}

func (self *WXBizMsgCrypt) EncryptMsg(reply_msg, timestamp, nonce string) ([]byte, *CryptError) {
	rand_str := self.randString(16)
	var buffer bytes.Buffer
	buffer.WriteString(rand_str)

	msg_len_buf := make([]byte, 4)
	binary.BigEndian.PutUint32(msg_len_buf, uint32(len(reply_msg)))
	buffer.Write(msg_len_buf)
	buffer.WriteString(reply_msg)
	buffer.WriteString(self.receiver_id)

	tmp_ciphertext, err := self.cbcEncrypter(buffer.String())
	if nil != err {
		return nil, err
	}
	ciphertext := string(tmp_ciphertext)

	signature := self.calSignature(timestamp, nonce, ciphertext)

	msg4_send := NewWXBizMsg4Send(ciphertext, signature, timestamp, nonce)
	return self.protocol_processor.serialize(msg4_send)
}

func (self *WXBizMsgCrypt) DecryptMsg(msg_signature, timestamp, nonce string, post_data []byte) ([]byte, *CryptError) {
	msg4_recv, crypt_err := self.protocol_processor.parse(post_data)
	if nil != crypt_err {
		return nil, crypt_err
	}

	signature := self.calSignature(timestamp, nonce, msg4_recv.Encrypt)

	if strings.Compare(signature, msg_signature) != 0 {
		return nil, NewCryptError(ValidateSignatureError, "signature not equal")
	}

	plaintext, crypt_err := self.cbcDecrypter(msg4_recv.Encrypt)
	if nil != crypt_err {
		return nil, crypt_err
	}

	_, _, msg, receiver_id, crypt_err := self.ParsePlainText(plaintext)
	if nil != crypt_err {
		return nil, crypt_err
	}

	if len(self.receiver_id) > 0 && strings.Compare(string(receiver_id), self.receiver_id) != 0 {
		return nil, NewCryptError(ValidateCorpidError, "receiver_id is not equil")
	}

	return msg, nil
}

// 把整数 n 格式化成 4 字节的网络字节序
func encodeNetworkByteOrder(b []byte, n uint32) {
	b[0] = byte(n >> 24)
	b[1] = byte(n >> 16)
	b[2] = byte(n >> 8)
	b[3] = byte(n)
}

func AESEncryptMsg(random, rawXMLMsg []byte, appId string, aesKey []byte) (ciphertext []byte) {
	const (
		BLOCK_SIZE = 32             // PKCS#7
		BLOCK_MASK = BLOCK_SIZE - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
	)

	appIdOffset := 20 + len(rawXMLMsg)
	contentLen := appIdOffset + len(appId)
	amountToPad := BLOCK_SIZE - contentLen&BLOCK_MASK
	plaintextLen := contentLen + amountToPad

	plaintext := make([]byte, plaintextLen)

	// 拼接
	copy(plaintext[:16], random)
	encodeNetworkByteOrder(plaintext[16:20], uint32(len(rawXMLMsg)))
	copy(plaintext[20:], rawXMLMsg)
	copy(plaintext[appIdOffset:], appId)

	// PKCS#7 补位
	for i := contentLen; i < plaintextLen; i++ {
		plaintext[i] = byte(amountToPad)
	}

	// 加密
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		fmt.Println(err)
		panic(err)
	}
	mode := cipher.NewCBCEncrypter(block, aesKey[:16])
	mode.CryptBlocks(plaintext, plaintext)

	ciphertext = plaintext
	return
}

func SHA1(token, timestamp, nonce, encrypt string) string {
	array := []string{token, timestamp, nonce, encrypt}
	sort.Strings(array)
	str := strings.Join(array, "")

	hash := sha1.New()
	hash.Write([]byte(str))
	sum := hash.Sum(nil)
	sumHex := make([]byte, hex.EncodedLen(len(sum)))
	hex.Encode(sumHex, sum)
	return string(sumHex)
}

type WechatCryptor struct {
	appId  string
	token  string
	aesKey []byte
}

// 构造函数
// appId 公众平台appid
// token 公众平台上，开发者设置的token
// aesKey 公众平台上，开发者设置的EncodingAESKey
func NewWechatCryptor(appId, token, encodingAesKey string) (*WechatCryptor, error) {
	cryptor := new(WechatCryptor)
	if 43 != len(encodingAesKey) {
		return cryptor, &WechatCryptorError{Code: IllegalAesKey}
	}
	cryptor.appId = appId
	cryptor.token = token
	cryptor.aesKey, _ = base64.StdEncoding.DecodeString(encodingAesKey + "=")
	return cryptor, nil
}

func (cryptor *WechatCryptor) String() string {
	return "AppId(" + cryptor.appId +
		") Token(" + cryptor.token +
		") AES_KEY(" + base64.StdEncoding.EncodeToString(cryptor.aesKey) + ")"
}

// 随机生成16位字符串
func WechatCryptorRandomStr() string {
	r := mathRand.New(mathRand.NewSource(time.Now().UnixNano()))
	bytes := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	var result []byte
	for i := 0; i < 16; i++ {
		result = append(result, bytes[r.Intn(len(bytes))])
	}
	return string(result)
}

const WechatCryptorEncryptMsgFormat = `
<xml>
<Encrypt><![CDATA[%s]]></Encrypt>
<MsgSignature><![CDATA[%s]]></MsgSignature>
<TimeStamp>%s</TimeStamp>
<Nonce><![CDATA[%s]]></Nonce>
</xml>
`

func (cryptor *WechatCryptor) EncryptMsg(msg, timeStamp, nonce string) (string, error) {
	encrypt, sign, timeStamp, nonce, err := cryptor.EncryptMsgContent(msg, timeStamp, nonce)
	if nil != err {
		return "", err
	}
	//fmt.Println(fmt.Sprintf(WechatCryptorEncryptMsgFormat, encrypt, sign, timeStamp, nonce))
	//return encrypt,nil
	encryptData := &EncryptData{}
	encryptData.EncryptedMsg = encrypt
	encryptData.MsgSignature = sign
	encryptData.Timestamp = timeStamp
	encryptData.Nonce = nonce
	return encrypt, nil

	//return fmt.Sprintf(WechatCryptorEncryptMsgFormat, encrypt, sign, timeStamp, nonce), nil
}

type WechatCryptorPostBody struct {
	XMLName    xml.Name `xml:"xml"`
	ToUserName string   `xml:"ToUserName"`
	AppId      string   `xml:"AppId"`
	Encrypt    string   `xml:"Encrypt"`
}

func (cryptor *WechatCryptor) DecryptMsg(msgSign, timeStamp, nonce, postData string) (string, error) {
	postBody := WechatCryptorPostBody{}
	err := xml.Unmarshal([]byte(postData), &postBody)
	if nil != err || 0 == len(postBody.Encrypt) {
		return "", &WechatCryptorError{Code: ParseXmlError}
	}

	return cryptor.DecryptMsgContent(msgSign, timeStamp, nonce, postBody.Encrypt)
}

func (cryptor *WechatCryptor) EncryptMsgContent(msg, timeStamp, nonce string) (string, string, string, string, error) {
	encrypt, err := cryptor.Encrypt(WechatCryptorRandomStr(), msg)
	if nil != err {
		return "", "", "", "", err
	}

	if 0 == len(timeStamp) {
		timeStamp = string(time.Now().Unix())
	}

	sign := SHA1(cryptor.token, timeStamp, nonce, encrypt)
	return encrypt, sign, timeStamp, nonce, nil
}

func (cryptor *WechatCryptor) DecryptMsgContent(msgSign, timeStamp, nonce, encrypt string) (string, error) {
	sign := SHA1(cryptor.token, timeStamp, nonce, encrypt)
	if msgSign != sign {
		return "", &WechatCryptorError{Code: ValidateSignatureError}
	}

	return cryptor.Decrypt(encrypt)
}

// 对明文进行加密
func (cryptor *WechatCryptor) Encrypt(randomStr, text string) (string, error) {
	randomBytes := []byte(randomStr)
	textBytes := []byte(text)
	networkBytes := wechatCryptorBuildNetworkBytesOrder(len(textBytes))
	appIdBytes := []byte(cryptor.appId)
	var unencrypted []byte
	unencrypted = append(unencrypted, randomBytes...)
	unencrypted = append(unencrypted, networkBytes...)
	unencrypted = append(unencrypted, textBytes...)
	unencrypted = append(unencrypted, appIdBytes...)
	encrypted, err := wechatCryptorEncrypt(unencrypted, cryptor.aesKey)
	if nil != err {
		return "", &WechatCryptorError{Code: EncryptAESError}
	}
	return encrypted, nil
}

// 对密文进行解密
func (cryptor *WechatCryptor) Decrypt(text string) (string, error) {
	original, err := wechatCryptorDecrypt(text, cryptor.aesKey)
	if nil != err {
		return "", &WechatCryptorError{Code: DecryptAESError}
	}
	networkBytes := original[16:20]
	textLen := wechatCryptorRecoverNetworkBytesOrder(networkBytes)
	textBytes := original[20 : 20+textLen]
	appIdBytes := original[20+textLen:]
	if cryptor.appId != string(appIdBytes) {
		return "", &WechatCryptorError{Code: ValidateAppidError}
	}
	return string(textBytes), nil
}

func wechatCryptorEncrypt(rawData, key []byte) (string, error) {
	data, err := wechatCryptorAesCBCEncrypt(rawData, key)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(data), nil
}

func wechatCryptorDecrypt(rawData string, key []byte) ([]byte, error) {
	data, err := base64.StdEncoding.DecodeString(rawData)
	if err != nil {
		return nil, err
	}
	dnData, err := wechatCryptorAesCBCDecrypt(data, key)
	if err != nil {
		return nil, err
	}
	return dnData, nil
}

// aes加密，填充秘钥key的16位
func wechatCryptorAesCBCEncrypt(rawData, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// 填充原文
	rawData = wechatCryptorPKCS7Padding(rawData)
	cipherText := make([]byte, len(rawData))
	// 初始向量IV
	iv := key[:16]

	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(cipherText, rawData)

	return cipherText, nil
}

// aes解密
func wechatCryptorAesCBCDecrypt(encryptData, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err)
	}

	// 初始向量IV
	iv := key[:16]
	mode := cipher.NewCBCDecrypter(block, iv)

	mode.CryptBlocks(encryptData, encryptData)
	// 解填充
	encryptData = wechatCryptorPKCS7UnPadding(encryptData)
	return encryptData, nil
}

func wechatCryptorPKCS7Padding(ciphertext []byte) []byte {
	amountToPad := 32 - (len(ciphertext) % 32)
	if 0 == amountToPad {
		amountToPad = 32
	}
	padChr := (byte)(amountToPad & 0xFF)
	result := make([]byte, len(ciphertext), len(ciphertext)+amountToPad)
	copy(result, ciphertext)
	for i := 0; i < amountToPad; i++ {
		result = append(result, padChr)
	}
	return result
}

func wechatCryptorPKCS7UnPadding(origData []byte) []byte {
	pad := (int)(origData[len(origData)-1])
	if pad < 1 || pad > 32 {
		pad = 0
	}
	return origData[:len(origData)-pad]
}

// 生成4个字节的网络字节序
func wechatCryptorBuildNetworkBytesOrder(number int) []byte {
	return []byte{
		(byte)(number >> 24 & 0xFF),
		(byte)(number >> 16 & 0xF),
		(byte)(number >> 8 & 0xFF),
		(byte)(number & 0xFF),
	}
}

// 还原4个字节的网络字节序
func wechatCryptorRecoverNetworkBytesOrder(orderBytes []byte) int {
	var number = 0
	for i := 0; i < 4; i++ {
		number <<= 8
		number |= (int)(orderBytes[i] & 0xff)
	}
	return number
}

/**
异常的错误码和具体的错误信息
*/
type WechatCryptorError struct {
	Code int
}

const OK int = 0
const ValidateAppidError int = -40005

func (e *WechatCryptorError) Error() string {
	switch e.Code {
	case ValidateSignatureError:
		return "签名验证错误"
	case ParseXmlError:
		return "xml解析失败"
	case ComputeSignatureError:
		return "sha加密生成签名失败"
	case IllegalAesKey:
		return "SymmetricKey非法"
	case ValidateAppidError:
		return "appid校验失败"
	case EncryptAESError:
		return "aes加密失败"
	case DecryptAESError:
		return "aes解密失败"
	case IllegalBuffer:
		return "解密后得到的buffer非法"
	default:
		return ""
	}
}

func aesKeyDecode(encodedAESKey string) (key []byte, err error) {
	if len(encodedAESKey) != 43 {
		err = fmt.Errorf("the length of encodedAESKey must be equal to 43")
		return
	}
	key, err = base64.StdEncoding.DecodeString(encodedAESKey + "=")
	if err != nil {
		return
	}
	if len(key) != 32 {
		err = fmt.Errorf("encodingAESKey invalid")
		return
	}
	return
}

// EncryptMsg 加密消息
func EncryptMsg(random, rawXMLMsg []byte, appID, aesKey string) (encrtptMsg []byte, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("panic error: err=%v", e)
			return
		}
	}()
	var key []byte
	key, err = aesKeyDecode(aesKey)
	if err != nil {
		panic(err)
	}
	ciphertext := AESEncryptMsgS(random, rawXMLMsg, appID, key)
	encrtptMsg = []byte(base64.StdEncoding.EncodeToString(ciphertext))
	return
}

// AESEncryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
// 参考：github.com/chanxuehong/wechat.v2
func AESEncryptMsgS(random, rawXMLMsg []byte, appID string, aesKey []byte) (ciphertext []byte) {
	const (
		BlockSize = 32            // PKCS#7
		BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
	)

	appIDOffset := 20 + len(rawXMLMsg)
	contentLen := appIDOffset + len(appID)
	amountToPad := BlockSize - contentLen&BlockMask
	plaintextLen := contentLen + amountToPad

	plaintext := make([]byte, plaintextLen)

	// 拼接
	copy(plaintext[:16], random)
	encodeNetworkByteOrder(plaintext[16:20], uint32(len(rawXMLMsg)))
	copy(plaintext[20:], rawXMLMsg)
	copy(plaintext[appIDOffset:], appID)

	// PKCS#7 补位
	for i := contentLen; i < plaintextLen; i++ {
		plaintext[i] = byte(amountToPad)
	}

	// 加密
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		panic(err)
	}
	mode := cipher.NewCBCEncrypter(block, aesKey[:16])
	mode.CryptBlocks(plaintext, plaintext)

	ciphertext = plaintext
	return
}

// Signature sha1签名
func Signature(params ...string) string {
	sort.Strings(params)
	h := sha1.New()
	for _, s := range params {
		_, _ = io.WriteString(h, s)
	}
	return fmt.Sprintf("%x", h.Sum(nil))
}

func Encrypt(encryptedStr string) (string, error) {
	// 安全模式下对消息进行加密
	var encryptedMsg []byte
	nonce := authorized.GetRandomString(10)
	encryptedMsg, err := EncryptMsg([]byte(WechatCryptorRandomStr()), []byte(encryptedStr), global.Cfg.Smart.SmartAppId, global.Cfg.Smart.SmartEncodingAESKey)
	if err != nil {
		return "", err
	}
	// TODO 如果获取不到timestamp nonce 则自己生成
	timestamp := time.Now().Unix()
	timestampStr := strconv.FormatInt(timestamp, 10)
	msgSignature := Signature(global.Cfg.Smart.SmartToken, timestampStr, nonce, string(encryptedMsg))
	replyMsg := ResponseEncryptedXMLMsg{
		EncryptedMsg: string(encryptedMsg),
		MsgSignature: msgSignature,
		Timestamp:    timestamp,
		Nonce:        nonce,
	}
	bytes, err := xml.Marshal(replyMsg)
	fmt.Println(string(bytes))
	return string(bytes), err
}

// DecryptMsg 消息解密
func DecryptMsg(appID, encryptedMsg, aesKey string) (random, rawMsgXMLBytes []byte, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("panic error: err=%v", e)
			return
		}
	}()
	var encryptedMsgBytes, key, getAppIDBytes []byte
	encryptedMsgBytes, err = base64.StdEncoding.DecodeString(encryptedMsg)
	if err != nil {
		return
	}
	key, err = aesKeyDecode(aesKey)
	if err != nil {
		panic(err)
	}
	random, rawMsgXMLBytes, getAppIDBytes, err = AESDecryptMsg(encryptedMsgBytes, key)
	if err != nil {
		err = fmt.Errorf("消息解密失败,%v", err)
		return
	}
	if appID != string(getAppIDBytes) {
		err = fmt.Errorf("消息解密校验APPID失败")
		return
	}
	return
}

// AESDecryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
// 参考：github.com/chanxuehong/wechat.v2
func AESDecryptMsg(ciphertext []byte, aesKey []byte) (random, rawXMLMsg, appID []byte, err error) {
	const (
		BlockSize = 32            // PKCS#7
		BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
	)

	if len(ciphertext) < BlockSize {
		err = fmt.Errorf("the length of ciphertext too short: %d", len(ciphertext))
		return
	}
	if len(ciphertext)&BlockMask != 0 {
		err = fmt.Errorf("ciphertext is not a multiple of the block size, the length is %d", len(ciphertext))
		return
	}

	plaintext := make([]byte, len(ciphertext)) // len(plaintext) >= BLOCK_SIZE

	// 解密
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		panic(err)
	}
	mode := cipher.NewCBCDecrypter(block, aesKey[:16])
	mode.CryptBlocks(plaintext, ciphertext)

	// PKCS#7 去除补位
	amountToPad := int(plaintext[len(plaintext)-1])
	if amountToPad < 1 || amountToPad > BlockSize {
		err = fmt.Errorf("the amount to pad is incorrect: %d", amountToPad)
		return
	}
	plaintext = plaintext[:len(plaintext)-amountToPad]

	// 反拼接
	// len(plaintext) == 16+4+len(rawXMLMsg)+len(appId)
	if len(plaintext) <= 20 {
		err = fmt.Errorf("plaintext too short, the length is %d", len(plaintext))
		return
	}
	rawXMLMsgLen := int(decodeNetworkByteOrder(plaintext[16:20]))
	if rawXMLMsgLen < 0 {
		err = fmt.Errorf("incorrect msg length: %d", rawXMLMsgLen)
		return
	}
	appIDOffset := 20 + rawXMLMsgLen
	if len(plaintext) <= appIDOffset {
		err = fmt.Errorf("msg length too large: %d", rawXMLMsgLen)
		return
	}

	random = plaintext[:16:20]
	rawXMLMsg = plaintext[20:appIDOffset:appIDOffset]
	appID = plaintext[appIDOffset:]
	return
}

// 从 4 字节的网络字节序里解析出整数
func decodeNetworkByteOrder(orderBytes []byte) (n uint32) {
	return uint32(orderBytes[0])<<24 |
		uint32(orderBytes[1])<<16 |
		uint32(orderBytes[2])<<8 |
		uint32(orderBytes[3])
}
